using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Serialization;
using Nsb;

namespace Yoshi.Fb
{
    public class FbNoteData : FbIdentity
    {
        private const string NoteName = "Yoshi Data Note";
        private const string NoteDelimiterBg = "(SAVETHIS(1[";
        private const string NoteDelimiterEn = "])SAVETHIS)";
        private string _dataNoteId;
        private const int MaxSize = 65535;

        internal void RetrieveBlob(FnDelegate1 fn)
        {
            RetrieveDataNote(NoteName, fn);
        }

        private void RetrieveDataNote(string name, FnDelegate1 fn)
        {
            GetCache(FbBaseUser.id, "notes",
                     delegate(object fbo)
                     {
                         _dataNoteId = null;
                         if (Script.IsValue(fbo))
                         {
                             FbNote[] notes = (FbNote[])((FbResult)fbo).data;
                             foreach (FbNote n in notes)
                             {
                                 if (n.subject != name)
                                     continue;
                                 try
                                 {
                                     FbNote note = n;
                                     _dataNoteId = note.id;
                                     //Inform.Trace("Data note content = [{0}]", note.message);
                                     object blob = Unpackage(note.message);
                                     if (Script.IsValue(fn))
                                         fn(blob);
                                 }
                                 catch (Exception ex)
                                 {
                                     Inform.Error(ex.ToString());
                                     if (Script.IsValue(fn))
                                         fn(null);
                                 }
                                 return;
                             }
                             CreateDataNote(NoteName, fn);
                         }
                     }
                );
        }

        [AlternateSignature]
        internal extern void SaveBlob(object blob);

        // ReSharper disable UnusedMember.Global
        internal void SaveBlob(object blob, FnDelegate fn)
        // ReSharper restore UnusedMember.Global
        {
            SaveDataNote(NoteName, blob);
        }

        private void SaveDataNote(string name, object blob)
        {
            Sleep.Wait(
                delegate
                {
                    if (!Script.IsValue(_dataNoteId))
                        throw new Exception("SaveDataNote null member");
                    Dictionary<string, object> values = new Dictionary<string, object>();
                    values["subject"] = name;
                    Dictionary<string, object> privacy = new Dictionary<string, object>();
                    privacy["value"] = "CUSTOM";
                    privacy["friends"] = "SELF";
                    values["privacy"] = Json.Stringify(privacy);
                    values["message"] = Repackage(blob);
                    Post(_dataNoteId, values, null,
                         delegate { Inform.Log("Data Note Updated"); }
                        );
                }, delegate { return Script.IsValue(_dataNoteId); }
                );
        }

        private void CreateDataNote(string name, FnDelegate1 fn)
        {
            Dictionary<string, object> values = new Dictionary<string, object>();
            values["subject"] = name;
            values["message"] = Repackage("{}");

            Dictionary<string, object> privacy = new Dictionary<string, object>();
            privacy["value"] = "CUSTOM";
            privacy["friends"] = "SELF";
            values["privacy"] = Json.Stringify(privacy);

            Post(FbBaseUser.id, values, "notes",
                 delegate(object fbData)
                 {
                     FbList l = (FbList)Json.Parse((string)fbData);
                     _dataNoteId = l.id;
                     Inform.Log("Data note id = {0}", _dataNoteId);
                     if (Script.IsValue(fn))
                         fn(null);
                 }
                );
        }

        //private string MakeBig()
        //{
        //    string ot = "";
        //    for (int i = 0; i < (70000 / 3); i++)
        //    {
        //        ot += "ab ";
        //    }
        //    return ot;
        //}


        private string Repackage(object o)
        {
            string json = Json.Stringify(o);
            string ot = NoteDelimiterBg + Encode(json) + NoteDelimiterEn;
            if (ot.Length > MaxSize)
            {
                const string msg = "Maximum data storage size exceeded";
                Inform.Error(msg);
                throw new Exception(msg);
            }
            //string lzw = (string)Script.Literal("Encoder.LzwEncode({0})", json);
            //string lzo = (string)Script.Literal("Encoder.LzwDecode({0})", lzw);
            //Inform.Trace("LZWized before={0}, after={1} \n{2}", json.Length, lzw.Length, lzw);
            //Inform.Trace("LZWrizd before={0}, after={1} \n{2}", lzw.Length, lzo.Length, lzo);
            return ot;
        }

        private object Unpackage(string s)
        {
            int st = s.IndexOf(NoteDelimiterBg) + NoteDelimiterBg.Length;
            if (st > 0)
            {
                int en = s.IndexOf(NoteDelimiterEn, st);
                if (en > st)
                {
                    string json = Decode(s.Substring(st, en));
                    return Json.Parse(json);
                }
            }
            return null;
        }

        private string Encode(string s)
        {
            return Encoder.Encode64(Encoder.LzwEncode(s));
        }

        private string Decode(string s)
        {
            return Encoder.LzwDecode(Encoder.Decode64(s));
        }
    }
}